`timescale 1ns / 1ps
/*
 Copyright 2020 Sean Xiao, jxzsxsp@qq.com
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
 http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

module exu_alu (
    input sys_clk,
    input rst_n,


    input [ 4: 0 ] i_rd_idx,
    input [ 31: 0 ] i_rs1_val,

    input [ 31: 0 ] i_PC,

    input i_OP_IMM,
    input i_LOAD,
    input [ 8: 0 ] i_opimm_instr,
    input [ 31: 0 ] i_I_imm,

    input i_OP,
    input [ 9: 0 ] i_op_instr,
    input [ 31: 0 ] i_rs2_val,

    input i_LUI,
    input i_AUIPC,
    input [ 31: 0 ] i_U_imm,

    input i_JAL,
    input i_JALR,
    input [ 31: 0 ] i_J_imm,

    input i_STORE,
    input [ 31: 0 ] i_S_imm,
//===============================================================================
    output o_J_vld,
    output [ 31: 0 ] o_J_PC,
//output addition result to LOAD/STORE unit    
    output [ 31: 0 ] o_D_PC,   

    output o_rd_wen,
    output [ 4: 0 ] o_wb_rd_idx,
    output reg [ 31: 0 ] o_wb_data

);

//===============================================================================
//reg     [31:0]      wb_data_opimm;
//reg     [31:0]      wb_data_op;
//reg [ 31: 0 ] wb_data_LUI;
//reg [ 31: 0 ] wb_data_AUIPC;

wire [ 31: 0 ] opd1 = ( i_AUIPC | i_JAL ) ? i_PC : i_rs1_val;

wire [ 31: 0 ] opd2 =   ( { 32{ i_OP_IMM | i_JALR | i_LOAD} } & i_I_imm ) |
                        ( { 32{ i_OP } } & i_rs2_val ) |
                        ( { 32{ i_JAL } } & i_J_imm ) |
                        ( { 32{ i_AUIPC } } & i_U_imm ) |
                        ( { 32{ i_STORE } } & i_S_imm ) ; //the second operand;
                        
//                              rv32i_slti    rv32i_slt
wire [ 33: 0 ] ext_opd1 = ({i_opimm_instr[1],i_op_instr[1]} != 0) ? {opd1[31],opd1[31],opd1} : {2'b00, opd1};
wire [ 33: 0 ] ext_opd2 = ({i_opimm_instr[1],i_op_instr[1]} != 0) ? {opd2[31],opd2[31],opd2} : {2'b00, opd2};

wire [ 33: 0 ] comp_opd2 = ~ext_opd2 + 32'b1; //2's complement

//                               rv32i_sub       rv32i_slti       rv32i_slt
//wire [ 33: 0 ] comp_ext_opd2 = ({i_op_instr[8], i_opimm_instr[1],i_op_instr[1]} != 0) ? comp_opd2 : ext_opd2;
wire [ 33: 0 ] comp_ext_opd2 = ({i_op_instr[8], i_opimm_instr[2:1],i_op_instr[2:1]} !=0)? comp_opd2 : ext_opd2;

wire [ 33: 0 ] add_res = ext_opd1 + comp_ext_opd2;

wire [ 31: 0 ] xor_res = i_rs1_val ^ opd2;
wire [ 31: 0 ] or_res  = i_rs1_val | opd2;
wire [ 31: 0 ] and_res = i_rs1_val & opd2;
wire [ 31: 0 ] sll_res = i_rs1_val << opd2[ 4: 0 ];
wire [ 31: 0 ] srl_res = i_rs1_val >> opd2[ 4: 0 ];
//===============================================================================
// shift right arithmetic immediate  x[rd] = ( x[rs1] >>s  shamt)
wire [ 31: 0 ] eff_mask = ( ~( 32'b0 ) ) >> opd2[ 4: 0 ];
wire [ 31: 0 ] sra_res = ( srl_res & eff_mask ) | ( { 32{ i_rs1_val[ 31 ] } } & ( ~eff_mask ) );

//wire [ 31: 0 ] sra_res = i_rs1_val >>> opd2[ 4: 0 ];

//===============================================================================
always@( * )
begin
    o_wb_data <= 32'b0;
/*
assign o_opimm_instr = { rv32i_srai,  rv32i_srli, rv32i_slli,
                         rv32i_andi,  rv32i_ori,  rv32i_xori,
                         rv32i_sltiu, rv32i_slti, rv32i_addi };
*/
    if ( i_OP_IMM )
    case ( i_opimm_instr )   //one hot decoder and execute
        9'h001: //rv32i_addi
            o_wb_data <= add_res[31:0];
        9'h002: //rv32i_slti
            o_wb_data <= add_res[ 33 ] ? 32'b1 : 32'b0;
        9'h004: //rv32i_sltiu
            o_wb_data <= add_res[ 33 ] ? 32'b1 : 32'b0;
        9'h008: //xori
            o_wb_data <= xor_res;
        9'h010: //rv32i_ori
            o_wb_data <= or_res;
        9'h020: //rv32i_andi
            o_wb_data <= and_res;
        9'h040: //rv32i_slli
            o_wb_data <= sll_res;
        9'h080: //rv32i_srli
            o_wb_data <= srl_res;
        9'h100: //rv32i_srai
            o_wb_data <= sra_res;
        default:   ;
    endcase
    
/*
assign o_op_instr = { rv32i_sra, rv32i_sub,  rv32i_srl,
                      rv32i_sll, rv32i_xor,  rv32i_or,
                      rv32i_and, rv32i_sltu, rv32i_slt,
                      rv32i_add };
*/    
    if ( i_OP )
    case ( i_op_instr )   //one hot decoder and execute
        10'h001:    //rv32i_add
            o_wb_data <= add_res[31:0];
        10'h002:    //rv32i_slt
            o_wb_data <= add_res[ 33 ] ? 32'b1 : 32'b0;
        10'h004:    //rv32i_sltu
            o_wb_data <= add_res[ 33 ] ? 32'b1 : 32'b0;
        10'h008:    //rv32i_and
            o_wb_data <= and_res;
        10'h010:    //rv32i_or
            o_wb_data <= or_res;
        10'h020:    //rv32i_xor
            o_wb_data <= xor_res;
        10'h040:    //rv32i_sll
            o_wb_data <= sll_res;
        10'h080:    //rv32i_srl
            o_wb_data <= srl_res;
        10'h100:    //rv32i_sub
            o_wb_data <= add_res[31:0];
        10'h200:    //rv32i_sra
            o_wb_data <= sra_res;
        default:  ;
    endcase

    if ( i_LUI )
        o_wb_data <= i_U_imm;

    if ( i_AUIPC )
        o_wb_data <= add_res[31:0];


    if ( i_JAL | i_JALR )
        o_wb_data <= i_PC + 4;

end
//===============================================================================

//  wire            rv32i_sub =o_op_instr[8];
//  wire            rv32i_sra =o_op_instr[9];

assign o_wb_rd_idx = i_rd_idx;
assign o_rd_wen = i_OP_IMM | i_OP | i_LUI | i_AUIPC | i_JAL | i_JALR;

assign o_D_PC = add_res[31:0];
assign o_J_PC = add_res[31:0];

assign o_J_vld = i_JAL | i_JALR;
//===============================================================================

endmodule
